﻿#region using statements

using System;

using AeonOfStrife.Misc;
using AeonOfStrife.Objects.Units;

using AeonOfStrifeDatatypes.Combat;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using NGen;
using NGen.Managers.GameObject;
using NGen.Systems.Graphics;

#endregion

namespace AeonOfStrife.Combat
{
	public class Projectile : GameObject2D, IAttacker
	{
		#region local fields

		protected const int c_SHOT_ANGLE = 45;

		protected const float c_SPEED_MULTIPLIER = 1;

		protected readonly AnimationInfo m_animation;

		protected AttackType m_attackType;

		protected readonly AnimatedSprite m_sprite;

		protected readonly Unit m_parentUnit;

		protected Vector2 m_initialPosition;

		protected float m_flightTime;

		protected TeamType m_team;

		protected readonly IAttackable m_target;

		protected Vector2 m_velocity;

		protected float m_range;

		public DamageValues Damage { get; protected set; }

		#endregion

		#region constructors

		public Projectile() {

			m_texture = GameEngine.GameContent.Load<Texture2D>("Content\\Test\\arrow");
			m_sprite = new AnimatedSprite(
				m_texture,
				new Point(100,25), 
				new Point(4,2), 
				UnitConstants.c_FRAME_DELAY);
			m_sprite.AddAnimation("flying", new Point(1,1), new Point(4,1));
			m_sprite.AddAnimation("grounded", new Point(1,2), new Point(1,2));
			m_sprite.ChangeAnimation("flying");
			//m_sprite.Origin = new Vector2(m_sprite.AnimationFrameSize.X / 2f, m_sprite.AnimationFrameSize.Y / 2f);
			m_sprite.FrameIncrementDelay = 0.05f;
			//m_speed = (float)Math.Sqrt((Constants.c_GRAVITY * 500) / (Math.Abs(Math.Sin(c_SHOT_ANGLE * 2))));
			m_position.X = 300;
			m_position.Y = 450;
			m_zDepth = 1;
			m_scale.X = 0.5f;
			m_scale.Y = 0.5f;
			//m_origin.X = m_texture.Bounds.Width / 2f;
			//m_origin.Y = m_texture.Bounds.Height / 2f;
			//m_textureOffset.X = m_origin.X;
			//m_textureOffset.Y = m_origin.Y;
			
		}

		public Projectile(DamageValues p_damage, AnimationInfo p_animation, 
			AttackType p_attackType, Unit p_parentUnit, IAttackable p_targetUnit) {
			Damage = p_damage;
			m_animation = p_animation;
			m_attackType = p_attackType;
			m_parentUnit = p_parentUnit;
			m_target = p_targetUnit;
			m_position = Vector2.Add(p_parentUnit.Position, p_parentUnit.m_origin);
			m_scale.X = 0.5f;
			m_scale.Y = 0.5f;
			m_sprite = new AnimatedSprite(
				GameEngine.GameContent.Load<Texture2D>(m_animation.m_spritesheet),
				m_animation.m_frameSize,
				m_animation.m_spritesheetDimensions,
				UnitConstants.c_FRAME_DELAY);
			m_sprite.Origin = new Vector2(m_sprite.AnimationFrameSize.X / 2f * m_scale.X, m_sprite.AnimationFrameSize.Y / 2f * m_scale.Y);
		}

		#endregion

		#region GameObject implementation

		protected override void _Initialize() {

			m_range = Math.Abs(m_position.X - m_target.Position.X);
			var speed = Math.Sqrt((Constants.c_GRAVITY * m_range) / (Math.Abs(Math.Sin(c_SHOT_ANGLE * 2))));

			if(m_position.X > m_target.Position.X) {
				m_spriteEffects = SpriteEffects.FlipHorizontally;
				m_angle = MathHelper.ToRadians(225);
			} else {
				m_spriteEffects = SpriteEffects.None;
				m_angle = MathHelper.ToRadians(315);
			}
			m_initialPosition = m_position;
			m_velocity.X = (float)(Math.Cos(m_angle) * speed);
			m_velocity.Y = (float)(Math.Sin(m_angle) * speed);
		}

		protected override void _Update(GameTime p_gameTime) {

			//stop moving if it hits the ground
			if(Math.Abs(m_position.X - m_initialPosition.X) > m_range) {
				Destroy(Id);
				return;
				//TODO change image to arrow stuck in ground
			}

			//TODO check for in range of target

			//gravity
			m_velocity.Y += (float)(Constants.c_GRAVITY * p_gameTime.ElapsedGameTime.TotalSeconds * c_SPEED_MULTIPLIER);

			m_position += Vector2.Multiply(m_velocity, (float) p_gameTime.ElapsedGameTime.TotalSeconds * c_SPEED_MULTIPLIER);
			
			//update angle of arrow to match movement direction
			m_angle = Math.Atan(m_velocity.Y / m_velocity.X);

			//TODO check if in range of target or reached max range and deal damage
			//AttackTarget(Damage);
		}

		protected override void _Draw(GameTime p_gameTime) {
			m_sprite.Update(p_gameTime);
			GameEngine.SpriteBatch.Draw(
					m_sprite.AnimationSpriteSheet,
					m_position,
					m_sprite.GetFrame(),
					Color.White,
					(float)m_angle,
					m_sprite.Origin,
					m_scale,
					m_spriteEffects,
					m_zDepth
				);

			//base._Draw(p_gameTime);
		}

		#endregion

		#region IAttacker implementation

		public IAttackable Target { get; protected set; }

		public event UnitAttackEvent ThrowAttackEvent;

		public void OnTargetDeath(IAttackable p_attackee) {
			ThrowAttackEvent -= p_attackee.OnAttacked;
		}

		public void AcquireTarget() {
			ThrowAttackEvent += Target.OnAttacked;
		}

		public void AttackTarget(DamageValues p_damage) {
			if(ThrowAttackEvent != null) {
				ThrowAttackEvent(m_parentUnit, p_damage);
			}
		}

		#endregion
	}
}